from typing import Dict, List, Tuple

from . import database


def analyze_overlaps(db, report_path, max_items: int = 100):
    rects = collect_cells_rects(db)
    n = len(rects)
    total_pairs = 0
    overlaps: List[Dict] = []
    for i in range(n):
        for j in range(i + 1, n):
            w_int, h_int, ix, iy = rect_intersection(rects[i], rects[j])
            if w_int > 0 and h_int > 0:
                area = w_int * h_int
                total_pairs += 1
                if len(overlaps) < max_items:
                    overlaps.append({
                        'a': rects[i]['name'],
                        'b': rects[j]['name'],
                        'area': area,
                        'ix': ix,
                        'iy': iy,
                        'w': w_int,
                        'h': h_int,
                    })
    placed_cnt = sum(1 for r in rects if r['placed'])
    fixed_cnt = sum(1 for r in rects if r['fixed'])
    macro_cnt = sum(1 for r in rects if r['is_macro'])
    std_cnt = n - macro_cnt

    print(f"[overlap] insts={n} placed={placed_cnt} fixed={fixed_cnt} macro={macro_cnt} std={std_cnt}")
    if total_pairs == 0:
        print("[overlap] no overlaps detected.")
    else:
        print(f"[overlap] total overlapping pairs: {total_pairs}, listed up to {len(overlaps)}")
        for k, it in enumerate(overlaps, 1):
            print(f"  #{k}: {it['a']} <-> {it['b']} area={it['area']} rect=({it['ix']},{it['iy']},{it['w']},{it['h']})")
    
    with open(report_path, 'w', encoding='utf-8') as fp:
        fp.write(f"insts={n} placed={placed_cnt} fixed={fixed_cnt} macro={macro_cnt} std={std_cnt}\n")
        fp.write(f"total_overlapping_pairs={total_pairs}\n")
        for it in overlaps:
            fp.write(f"{it['a']},{it['b']},{it['area']},{it['ix']},{it['iy']},{it['w']},{it['h']}\n")


def collect_cells_rects(db) -> List[Dict]:
    rects = []
    for name, inst in db.insts.items():
        if not inst.get('placed') and not inst.get('fixed'):
            continue
        macro = inst.get('macro', '')
        w_um, h_um = db.macros_um.get(macro, (0.0, 0.0))
        w = int(round(w_um * db.dbu_per_micron))
        h = int(round(h_um * db.dbu_per_micron))
        o = inst.get('orient', 'N')
        if o in ('E', 'W', 'FE', 'FW'):
            w, h = h, w
        if w <= 0 or h <= 0:
            continue
        lx = inst.get('x', 0)
        ly = inst.get('y', 0)
        rects.append({
            'name': name,
            'macro': macro,
            'fixed': bool(inst.get('fixed', False)),
            'placed': bool(inst.get('placed', False)),
            'is_macro': is_macro_cell(db, macro),
            'lx': lx,
            'ly': ly,
            'ux': lx + w,
            'uy': ly + h,
            'w': w,
            'h': h,
            'orient': o,
        })
    return rects


def color_for(name: str) -> str:
    h = 0
    for c in name:
        h = (h * 131 + ord(c)) & 0xFFFFFF
    r = 128 + (h & 0x7F)
    g = 128 + ((h >> 7) & 0x7F)
    b = 128 + ((h >> 14) & 0x7F)
    return f"#{r:02X}{g:02X}{b:02X}"


def rect_intersection(a: Dict, b: Dict) -> Tuple[int, int, int, int]:
    xx1 = max(a['lx'], b['lx'])
    yy1 = max(a['ly'], b['ly'])
    xx2 = min(a['ux'], b['ux'])
    yy2 = min(a['uy'], b['uy'])
    if xx2 > xx1 and yy2 > yy1:
        return (xx2 - xx1), (yy2 - yy1), xx1, yy1
    return 0, 0, 0, 0


def is_macro_cell(db, macro: str) -> bool:
    cls = db.macros_class.get(macro, '').upper()
    return (cls in ('BLOCK', 'PAD')) or (cls == '' and not macro.startswith('sky130_fd_sc_'))




